{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Pandas 字符串处理\n",
        "\n",
        "本教程详细介绍如何在 Pandas 中处理字符串数据。\n",
        "\n",
        "## 目录\n",
        "1. 字符串访问器 (str)\n",
        "2. 字符串大小写转换\n",
        "3. 字符串清理\n",
        "4. 字符串分割和拼接\n",
        "5. 字符串查找和替换\n",
        "6. 字符串格式化\n",
        "7. 高级技巧"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 导入库"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "import numpy as np"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1. 字符串访问器 (str)\n",
        "\n",
        "### 方法说明\n",
        "\n",
        "Pandas 提供了 `.str` 访问器来处理字符串数据。\n",
        "\n",
        "**语法:**\n",
        "```python\n",
        "df['列名'].str.方法名()\n",
        "```\n",
        "\n",
        "**特点：**\n",
        "- ✅ 类似 Python 字符串方法\n",
        "- ✅ 自动处理 NaN 值\n",
        "- ✅ 向量化操作，性能好\n",
        "- ✅ 返回 Series 或 DataFrame\n",
        "\n",
        "**适用场景：** 所有字符串操作"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 创建示例数据"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始 DataFrame:\n",
            "     姓名                 邮箱             电话       地址         描述\n",
            "0   张三   zhang@example.com   010-12345678   北京市朝阳区  Python开发者\n",
            "1    李四  LI_SI@EXAMPLE.COM   021-87654321  上海市浦东新区      数据分析师\n",
            "2    王五   wang.wu@test.com  0755-11223344   深圳市南山区    机器学习工程师\n",
            "3    赵六   zhaoliu@demo.com   029-55667788   西安市雁塔区      数据科学家\n"
          ]
        }
      ],
      "source": [
        "df = pd.DataFrame({\n",
        "    '姓名': [' 张三 ', '李四', '王五', '赵六'],\n",
        "    '邮箱': ['zhang@example.com', 'LI_SI@EXAMPLE.COM', 'wang.wu@test.com', 'zhaoliu@demo.com'],\n",
        "    '电话': ['010-12345678', '021-87654321', '0755-11223344', '029-55667788'],\n",
        "    '地址': ['北京市朝阳区', '上海市浦东新区', '深圳市南山区', '西安市雁塔区'],\n",
        "    '描述': ['Python开发者', '数据分析师', '机器学习工程师', '数据科学家']\n",
        "})\n",
        "\n",
        "print(\"原始 DataFrame:\")\n",
        "print(df)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. 字符串大小写转换\n",
        "\n",
        "### 方法说明\n",
        "\n",
        "常用的字符串大小写转换方法。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 转小写 (lower)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "转小写:\n",
            "                  邮箱              邮箱_小写\n",
            "0  zhang@example.com  zhang@example.com\n",
            "1  LI_SI@EXAMPLE.COM  li_si@example.com\n",
            "2   wang.wu@test.com   wang.wu@test.com\n",
            "3   zhaoliu@demo.com   zhaoliu@demo.com\n"
          ]
        }
      ],
      "source": [
        "# 转小写\n",
        "df['邮箱_小写'] = df['邮箱'].str.lower()\n",
        "print(\"转小写:\")\n",
        "print(df[['邮箱', '邮箱_小写']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例2: 转大写 (upper)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "转大写:\n",
            "                  邮箱              邮箱_大写\n",
            "0  zhang@example.com  ZHANG@EXAMPLE.COM\n",
            "1  LI_SI@EXAMPLE.COM  LI_SI@EXAMPLE.COM\n",
            "2   wang.wu@test.com   WANG.WU@TEST.COM\n",
            "3   zhaoliu@demo.com   ZHAOLIU@DEMO.COM\n"
          ]
        }
      ],
      "source": [
        "# 转大写\n",
        "df['邮箱_大写'] = df['邮箱'].str.upper()\n",
        "print(\"转大写:\")\n",
        "print(df[['邮箱', '邮箱_大写']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例3: 首字母大写 (title, capitalize)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "title 方法（每个单词首字母大写）:\n",
            "     姓名 姓名_title\n",
            "0   张三       张三 \n",
            "1    李四       李四\n",
            "2    王五       王五\n",
            "3    赵六       赵六\n",
            "\n",
            "capitalize 方法（第一个字符大写）:\n",
            "     姓名 姓名_capitalize\n",
            "0   张三            张三 \n",
            "1    李四            李四\n",
            "2    王五            王五\n",
            "3    赵六            赵六\n",
            "\n",
            "swapcase 方法（大小写互换）:\n",
            "                  邮箱        邮箱_swapcase\n",
            "0  zhang@example.com  ZHANG@EXAMPLE.COM\n",
            "1  LI_SI@EXAMPLE.COM  li_si@example.com\n",
            "2   wang.wu@test.com   WANG.WU@TEST.COM\n",
            "3   zhaoliu@demo.com   ZHAOLIU@DEMO.COM\n"
          ]
        }
      ],
      "source": [
        "# title: 每个单词首字母大写\n",
        "df['姓名_title'] = df['姓名'].str.title()\n",
        "print(\"title 方法（每个单词首字母大写）:\")\n",
        "print(df[['姓名', '姓名_title']])\n",
        "\n",
        "# capitalize: 第一个字符大写，其他小写\n",
        "df['姓名_capitalize'] = df['姓名'].str.capitalize()\n",
        "print(\"\\ncapitalize 方法（第一个字符大写）:\")\n",
        "print(df[['姓名', '姓名_capitalize']])\n",
        "\n",
        "# swapcase: 大小写互换\n",
        "df['邮箱_swapcase'] = df['邮箱'].str.swapcase()\n",
        "print(\"\\nswapcase 方法（大小写互换）:\")\n",
        "print(df[['邮箱', '邮箱_swapcase']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. 字符串清理\n",
        "\n",
        "### 方法说明\n",
        "\n",
        "清理字符串中的空格、特殊字符等。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 去除空格 (strip, lstrip, rstrip)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "去除两端空格:\n",
            "     姓名 姓名_去除两端空格\n",
            "0   张三         张三\n",
            "1    李四        李四\n",
            "2    王五        王五\n",
            "3    赵六        赵六\n",
            "\n",
            "去除左边空格:\n",
            "     姓名 姓名_去除左边空格\n",
            "0   张三        张三 \n",
            "1    李四        李四\n",
            "2    王五        王五\n",
            "3    赵六        赵六\n",
            "\n",
            "去除右边空格:\n",
            "     姓名 姓名_去除右边空格\n",
            "0   张三         张三\n",
            "1    李四        李四\n",
            "2    王五        王五\n",
            "3    赵六        赵六\n"
          ]
        }
      ],
      "source": [
        "# strip: 去除两端空格\n",
        "df['姓名_去除两端空格'] = df['姓名'].str.strip()\n",
        "print(\"去除两端空格:\")\n",
        "print(df[['姓名', '姓名_去除两端空格']])\n",
        "\n",
        "# lstrip: 去除左边空格\n",
        "df['姓名_去除左边空格'] = df['姓名'].str.lstrip()\n",
        "print(\"\\n去除左边空格:\")\n",
        "print(df[['姓名', '姓名_去除左边空格']])\n",
        "\n",
        "# rstrip: 去除右边空格\n",
        "df['姓名_去除右边空格'] = df['姓名'].str.rstrip()\n",
        "print(\"\\n去除右边空格:\")\n",
        "print(df[['姓名', '姓名_去除右边空格']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例2: 去除指定字符"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_chars = pd.DataFrame({\n",
        "    '文本': ['---abc---', '===def===', '***ghi***']\n",
        "})\n",
        "\n",
        "print(\"原始数据:\")\n",
        "print(df_chars)\n",
        "\n",
        "# 去除指定字符\n",
        "df_chars['去除-'] = df_chars['文本'].str.strip('-')\n",
        "df_chars['去除='] = df_chars['文本'].str.strip('=')\n",
        "df_chars['去除*'] = df_chars['文本'].str.strip('*')\n",
        "\n",
        "print(\"\\n去除指定字符:\")\n",
        "print(df_chars)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例3: 替换空格"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 替换所有空格\n",
        "df['地址_无空格'] = df['地址'].str.replace(' ', '')\n",
        "print(\"替换空格:\")\n",
        "print(df[['地址', '地址_无空格']])\n",
        "\n",
        "# 替换多个空格为单个空格\n",
        "df_space = pd.DataFrame({'文本': ['a   b    c', 'x    y   z']})\n",
        "df_space['单空格'] = df_space['文本'].str.replace(r'\\s+', ' ', regex=True)\n",
        "print(\"\\n替换多个空格为单个空格:\")\n",
        "print(df_space)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. 字符串分割和拼接\n",
        "\n",
        "### 方法说明\n",
        "\n",
        "分割和拼接是常见的字符串操作。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 分割字符串 (split)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 分割字符串\n",
        "df['电话_分割'] = df['电话'].str.split('-')\n",
        "print(\"分割字符串:\")\n",
        "print(df[['电话', '电话_分割']])\n",
        "\n",
        "# 分割并展开为多列\n",
        "df_phone_split = df['电话'].str.split('-', expand=True)\n",
        "df_phone_split.columns = ['区号', '号码']\n",
        "print(\"\\n分割并展开为多列:\")\n",
        "print(df_phone_split)\n",
        "\n",
        "# 限制分割次数\n",
        "df['邮箱_分割'] = df['邮箱'].str.split('@', n=1, expand=True)[0]  # 只分割一次，取第一部分\n",
        "print(\"\\n限制分割次数（取@前的部分）:\")\n",
        "print(df[['邮箱', '邮箱_分割']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例2: 拼接字符串 (cat, +)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 使用 + 拼接\n",
        "df['姓名电话'] = df['姓名'].str.strip() + ' - ' + df['电话']\n",
        "print(\"拼接字符串:\")\n",
        "print(df[['姓名', '电话', '姓名电话']])\n",
        "\n",
        "# 使用 cat 方法拼接\n",
        "df['拼接_cat'] = df['姓名'].str.strip().str.cat(df['电话'], sep=' | ')\n",
        "print(\"\\n使用 cat 方法拼接:\")\n",
        "print(df[['拼接_cat']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例3: 提取子字符串 (slice)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 提取前N个字符\n",
        "df['姓名_前2'] = df['姓名'].str[:2]\n",
        "print(\"提取前2个字符:\")\n",
        "print(df[['姓名', '姓名_前2']])\n",
        "\n",
        "# 提取后N个字符\n",
        "df['电话_后4'] = df['电话'].str[-4:]\n",
        "print(\"\\n提取后4个字符:\")\n",
        "print(df[['电话', '电话_后4']])\n",
        "\n",
        "# 提取指定范围\n",
        "df['地址_前3'] = df['地址'].str[0:3]\n",
        "print(\"\\n提取前3个字符:\")\n",
        "print(df[['地址', '地址_前3']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 5. 字符串查找和替换\n",
        "\n",
        "### 方法说明\n",
        "\n",
        "查找和替换是常见的字符串操作。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 查找字符串 (contains, startswith, endswith)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# contains: 包含子字符串\n",
        "df['包含北京'] = df['地址'].str.contains('北京')\n",
        "print(\"包含'北京':\")\n",
        "print(df[['地址', '包含北京']])\n",
        "\n",
        "# startswith: 以指定字符串开头\n",
        "df['以市开头'] = df['地址'].str.startswith('市')\n",
        "print(\"\\n以'市'开头:\")\n",
        "print(df[['地址', '以市开头']])\n",
        "\n",
        "# endswith: 以指定字符串结尾\n",
        "df['以区结尾'] = df['地址'].str.endswith('区')\n",
        "print(\"\\n以'区'结尾:\")\n",
        "print(df[['地址', '以区结尾']])\n",
        "\n",
        "# 使用正则表达式\n",
        "df['包含数字'] = df['电话'].str.contains(r'\\d+', regex=True)\n",
        "print(\"\\n包含数字（使用正则）:\")\n",
        "print(df[['电话', '包含数字']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例2: 查找位置 (find, index)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# find: 查找子字符串位置（找不到返回-1）\n",
        "df['@位置'] = df['邮箱'].str.find('@')\n",
        "print(\"查找'@'的位置:\")\n",
        "print(df[['邮箱', '@位置']])\n",
        "\n",
        "# index: 查找子字符串位置（找不到抛出异常）\n",
        "df['_位置'] = df['邮箱'].str.index('_', default=-1)  # default参数避免异常\n",
        "print(\"\\n查找'_'的位置:\")\n",
        "print(df[['邮箱', '_位置']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例3: 替换字符串 (replace)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 替换子字符串\n",
        "df['电话_替换'] = df['电话'].str.replace('-', '')\n",
        "print(\"替换'-'为空:\")\n",
        "print(df[['电话', '电话_替换']])\n",
        "\n",
        "# 使用正则表达式替换\n",
        "df['邮箱_替换'] = df['邮箱'].str.replace(r'\\.', '_', regex=True)\n",
        "print(\"\\n替换'.'为'_'（使用正则）:\")\n",
        "print(df[['邮箱', '邮箱_替换']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 6. 字符串格式化\n",
        "\n",
        "### 方法说明\n",
        "\n",
        "格式化字符串，使其符合特定格式。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例1: 长度和填充 (len, pad, zfill)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# len: 获取字符串长度\n",
        "df['姓名长度'] = df['姓名'].str.len()\n",
        "print(\"字符串长度:\")\n",
        "print(df[['姓名', '姓名长度']])\n",
        "\n",
        "# pad: 填充字符串\n",
        "df['姓名_左填充'] = df['姓名'].str.pad(width=10, side='left', fillchar='_')\n",
        "print(\"\\n左填充:\")\n",
        "print(df[['姓名', '姓名_左填充']])\n",
        "\n",
        "# zfill: 用0左填充\n",
        "df_id = pd.DataFrame({'ID': ['1', '2', '10', '100']})\n",
        "df_id['ID_填充'] = df_id['ID'].str.zfill(5)\n",
        "print(\"\\n用0左填充（zfill）:\")\n",
        "print(df_id)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例2: 提取匹配 (extract, extractall)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# extract: 提取匹配的第一个\n",
        "df['区号'] = df['电话'].str.extract(r'(\\d+)-', expand=False)\n",
        "print(\"提取区号:\")\n",
        "print(df[['电话', '区号']])\n",
        "\n",
        "# extractall: 提取所有匹配\n",
        "df_pattern = pd.DataFrame({'文本': ['电话:010-12345678 手机:13800138000']})\n",
        "df_extracted = df_pattern['文本'].str.extractall(r'(\\d+)')\n",
        "print(\"\\n提取所有数字:\")\n",
        "print(df_extracted)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 示例3: 计数 (count)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# count: 统计子字符串出现次数\n",
        "df['包含-数量'] = df['电话'].str.count('-')\n",
        "print(\"统计'-'出现次数:\")\n",
        "print(df[['电话', '包含-数量']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 7. 高级技巧\n",
        "\n",
        "### 技巧1: 链式操作"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 链式操作：去除空格 -> 转小写 -> 替换\n",
        "df['邮箱_清理'] = df['邮箱'].str.strip().str.lower().str.replace('_', '.')\n",
        "print(\"链式操作:\")\n",
        "print(df[['邮箱', '邮箱_清理']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 技巧2: 条件替换"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 根据条件替换\n",
        "df['等级'] = pd.Series(['A', 'B', 'C', 'D'])\n",
        "df['等级_中文'] = df['等级'].str.replace('A', '优秀').str.replace('B', '良好').str.replace('C', '中等').str.replace('D', '及格')\n",
        "print(\"条件替换:\")\n",
        "print(df[['等级', '等级_中文']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 技巧3: 处理NaN值"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "df_nan = pd.DataFrame({\n",
        "    '文本': ['abc', 'def', None, 'ghi']\n",
        "})\n",
        "\n",
        "print(\"原始数据（包含NaN）:\")\n",
        "print(df_nan)\n",
        "\n",
        "# str访问器自动处理NaN\n",
        "df_nan['大写'] = df_nan['文本'].str.upper()\n",
        "print(\"\\n使用str访问器（自动处理NaN）:\")\n",
        "print(df_nan)\n",
        "\n",
        "# 可以指定NaN的填充值\n",
        "df_nan['填充NaN'] = df_nan['文本'].str.upper().fillna('缺失')\n",
        "print(\"\\n填充NaN:\")\n",
        "print(df_nan)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 技巧4: 应用自定义函数"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 应用自定义函数\n",
        "def format_phone(phone):\n",
        "    \"\"\"格式化电话号码\"\"\"\n",
        "    return phone.replace('-', ' ') if phone else phone\n",
        "\n",
        "df['电话_格式化'] = df['电话'].str.replace('-', ' ')\n",
        "print(\"应用自定义格式化:\")\n",
        "print(df[['电话', '电话_格式化']])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 总结\n",
        "\n",
        "### 常用方法\n",
        "\n",
        "| 方法 | 功能 | 示例 |\n",
        "|------|------|------|\n",
        "| `str.lower()` | 转小写 | 'ABC' → 'abc' |\n",
        "| `str.upper()` | 转大写 | 'abc' → 'ABC' |\n",
        "| `str.strip()` | 去除空格 | ' abc ' → 'abc' |\n",
        "| `str.split()` | 分割字符串 | 'a-b-c' → ['a', 'b', 'c'] |\n",
        "| `str.replace()` | 替换字符串 | 'a-b' → 'a b' |\n",
        "| `str.contains()` | 包含检查 | 返回布尔值 |\n",
        "| `str.startswith()` | 开头检查 | 返回布尔值 |\n",
        "| `str.endswith()` | 结尾检查 | 返回布尔值 |\n",
        "| `str.len()` | 长度 | 返回长度 |\n",
        "| `str.extract()` | 提取匹配 | 返回匹配的字符串 |\n",
        "\n",
        "### 关键要点\n",
        "\n",
        "1. **使用str访问器**：所有字符串操作都通过 `.str` 访问器\n",
        "2. **链式操作**：可以连续调用多个字符串方法\n",
        "3. **自动处理NaN**：str访问器自动处理缺失值\n",
        "4. **正则表达式**：支持正则表达式进行模式匹配\n",
        "5. **向量化操作**：比Python原生字符串方法性能更好\n",
        "6. **提取和分割**：可以使用 `extract()` 和 `split()` 提取信息\n",
        "7. **大小写转换**：使用 `lower()`, `upper()`, `title()` 等方法\n",
        "8. **空格处理**：使用 `strip()`, `lstrip()`, `rstrip()` 清理空格"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "ml311",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}
